package com.jiwei.community.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jiwei.community.constants.SystemConstant;
import com.jiwei.community.dao.PostMapper;
import com.jiwei.community.dto.AddPostDto;
import com.jiwei.community.dto.PostDetailDto;
import com.jiwei.community.entity.*;
import com.jiwei.community.enums.AppHttpCodeEnum;
import com.jiwei.community.exception.SystemException;
import com.jiwei.community.service.*;
import com.jiwei.community.utils.RecommendUtils;
import com.jiwei.community.utils.RedisCache;
import com.jiwei.community.utils.SecurityUtils;
import com.jiwei.community.utils.SensitiveWordUtils;
import com.jiwei.community.vo.PageListVo;
import com.jiwei.community.vo.PostVo;
import com.jiwei.community.vo.ResponseResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * (Post)表服务实现类
 *
 * @author jiwei
 * @since 2024-03-20 11:00:01
 */
@Service("postService")
public class PostServiceImpl extends ServiceImpl<PostMapper, Post> implements PostService {
    @Resource
    PostMapper postMapper;
    @Resource
    CommunityService communityService;
    @Autowired
    RedisCache redisCache;
    @Autowired
    RedisTemplate redisTemplate;
    RecommendService recommendService = new RecommendServiceImpl();
    @Resource
    UserCommunityLevelService userCommunityLevelService;
    @Resource
    LikeService likeService;
    @Resource
    DynamicService dynamicService;
    @Autowired
    UserService userService;
    @Override
    public ResponseResult publish(AddPostDto post) {
        Long userId = SecurityUtils.getUserId();
        User user = userService.getById(userId);
        if (user.getStatus().equals(1)){
            throw new SystemException(AppHttpCodeEnum.FAILURE,"用户已被封禁");
        }
        int isJoined = userCommunityLevelService.count(new LambdaQueryWrapper<UserCommunityLevel>().
                eq(UserCommunityLevel::getCommunityId,post.getCommunityId()).
                eq(UserCommunityLevel::getUserId,SecurityUtils.getUserId()));
        if (isJoined <=0){
            return ResponseResult.errorResult(AppHttpCodeEnum.FAILURE,"尚未加入该社区");
        }
        Post newPost = new Post();
        newPost.setContent(post.getContent());
        newPost.setCoverPic(post.getCoverPic());
        if (post.getSectionId() != null && post.getSectionId()!=-1){
            newPost.setSectionId(post.getSectionId());
        }
        newPost.setTitle(post.getTitle());
        newPost.setCommunityId(post.getCommunityId());
        int count = postMapper.insert(newPost);
        if (post.isShare() && count>0){
            Dynamic dynamic = new Dynamic();
            dynamic.setShareId(newPost.getId());
            dynamic.setPubType(post.getDynamicType());
            dynamicService.save(dynamic);
        }
        //异步修改社区的帖子数量值
        communityService.updatePostNum(post.getCommunityId(),1L);
        return ResponseResult.okResult(newPost,"添加成功");
    }
    @Override
    public ResponseResult getPostList(Integer pageNum, Integer pageSize, Long communityId, Integer sectionId, Integer isTop, Integer isEssence) {
        Page<PostVo> page = new Page<>();
        page.setCurrent(pageNum);
        page.setSize(pageSize);

        postMapper.getPostList(page,communityId,sectionId,isTop,isEssence);
        Map<String,Object> map = new HashMap<>();

        //对帖子标题进行屏蔽词过滤
        List<PostVo> safePostList =  page.getRecords().stream().peek(postVo -> {
            String safeTitle = SensitiveWordUtils.replaceWord(postVo.getTitle(),SensitiveWordUtils.root);
            postVo.setTitle(safeTitle);
//            String safeContentPre = SensitiveWordUtils.replaceWord(postVo)
        }).collect(Collectors.toList());

        map.put("rows",safePostList);
        map.put("count",page.getTotal());
        return ResponseResult.okResult(map);
    }

    @Override
    public ResponseResult likePost(Long postId, Long userId) {
        Post post = getOne(new LambdaQueryWrapper<Post>().eq(Post::getId,postId));
        if (post==null) {
            throw new SystemException(AppHttpCodeEnum.FAILURE,"帖子不存在");
        }
        Like like = new Like();
        like.setUserId(userId);
        like.setLikedId(postId);
        like.setLikedType(0);//0帖子1动态

        try{
            likeService.save(like);
        }catch (DuplicateKeyException e){
            return ResponseResult.errorResult(AppHttpCodeEnum.FAILURE,"已点赞，不可重复点赞");
        }
        updateLikedNum(post,1);
        updateScoreByLike(userId,postId,1);
        return ResponseResult.okResult("点赞成功");
    }

    @Override
    public ResponseResult cancelLike(Long postId, Long userId) {
        Post post = getOne(new LambdaQueryWrapper<Post>().eq(Post::getId,postId));
        if (post==null) {
            throw new SystemException(AppHttpCodeEnum.FAILURE,"帖子不存在");
        }
        boolean isOk = false;
        isOk = likeService.remove(new LambdaQueryWrapper<Like>().eq(Like::getUserId,userId).eq(Like::getLikedId,postId).eq(Like::getLikedType,0));
        if (isOk){
            updateLikedNum(post,-1);
//            return ResponseResult.okResult("取消点赞成功");
        }else {
//            return ResponseResult.errorResult(AppHttpCodeEnum.FAILURE,"取消点赞失败");
        }
        updateScoreByLike(userId,postId,-1);
        return ResponseResult.okResult("取消点赞成功");
    }


    public void updateLikedNum(Post post,Integer increase){
        Integer likedNum = redisCache.getCacheMapValue("post:liked-num",post.getId()+"");
        if (likedNum == null){

            likedNum = post.getLikedNum();
        }
        likedNum+=increase;
        updateRedisLikedNumAsync(post.getId(),likedNum);
    }
    @Async("redisStatisticsExecutor")
    void updateRedisLikedNumAsync(Long postId, Integer newVal){
        redisCache.setCacheMapValue("post:liked-num",postId+"",newVal);
    }

    @Override
    public ResponseResult getPostDetail(Long postId) {
        PostDetailDto postDetailDto = postMapper.getDetailById(postId);
        if (postDetailDto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.FAILURE,"帖子不存在");
        }
        if (postDetailDto.getStatus().equals(1))return ResponseResult.errorResult(AppHttpCodeEnum.FAILURE,"该帖子已被屏蔽");

        postDetailDto.setTitle(SensitiveWordUtils.replaceWord(postDetailDto.getTitle()));
        postDetailDto.setContent(SensitiveWordUtils.replaceWord(postDetailDto.getContent()));
        return ResponseResult.okResult(postDetailDto);
    }

    @Override
    public void saveCacheCollectNum() {
        Map<String,Integer> map = redisCache.getCacheMap(SystemConstant.REDIS_PREFIX_POST_COLLECT_NUM);
        List<Post> posts = map.entrySet().stream().map(item->{
            Post post = new Post();
            post.setId(Long.parseLong(item.getKey()));
            post.setCollectNum(item.getValue());
            return post;
        }).collect(Collectors.toList());
        updateBatchById(posts);
    }

    @Override
    public void saveCacheLikedNum() {
        Map<String,Integer> map = redisCache.getCacheMap(SystemConstant.REDIS_PREFIX_POST_LIKED_NUM);
        List<Post> posts = map.entrySet().stream().map(item->{
            Post post = new Post();
            post.setId(Long.parseLong(item.getKey()));
            post.setLikedNum(item.getValue());
            return post;
        }).collect(Collectors.toList());
        updateBatchById(posts);
    }

    @Override
    public void saveCacheViewNum() {
        Map<String,Integer> map = redisCache.getCacheMap(SystemConstant.REDIS_PREFIX_POST_VIEW_NUM);
        List<Post> posts = map.entrySet().stream().map(item->{
            Post post = new Post();
            post.setId(Long.parseLong(item.getKey()));
            post.setViewNum(item.getValue());
            return post;
        }).collect(Collectors.toList());
        updateBatchById(posts);
    }

    @Override
    public void saveCacheCommentNum() {
        Map<String,Integer> map = redisCache.getCacheMap(SystemConstant.REDIS_PREFIX_POST_COMMENT_NUM);
        List<Post> posts = map.entrySet().stream().map(item->{
            Post post = new Post();
            post.setId(Long.parseLong(item.getKey()));
            post.setCommentNum(item.getValue());
            return post;
        }).collect(Collectors.toList());
        updateBatchById(posts);
    }


    @Override
    public ResponseResult updateViewCount(Long postId) {
        // 采用redis机制更新帖子的浏览量
        Integer cacheViewNum = updateCacheViewNum(postId,1);
        //不才用redis机制的更新浏览量方法
//        Post post = getById(postId);
//        post.setViewNum(post.getViewNum()+1);
//        Integer cacheViewNum = post.getViewNum();
//        updateById(post);
        Long userId = null;
        try{
            userId = SecurityUtils.getUserId();
            if (userId != null){//更新评分
                updateScoreByView(userId,postId);
            }
        }catch (SystemException ignored){
            ignored.printStackTrace();
        }

        return ResponseResult.okResult(cacheViewNum);
    }


    public Integer updateCacheViewNum(Long postId,Integer increase){
        Integer viewNum = redisCache.getCacheMapValue("post:view-num",postId+"");
        if (viewNum == null){//若redis没有获取到缓存则从数据库中获取
            Post post = postMapper.selectOne(new LambdaQueryWrapper<Post>().eq(Post::getId,postId));
            if (post != null) {
                viewNum = post.getViewNum();
                viewNum+=increase;
                updateRedisViewNumAsync(postId,viewNum);
            }
        }else{
            viewNum+=increase;
            updateRedisViewNumAsync(postId,viewNum);
        }

        return viewNum;
    }
    @Async("redisStatisticsExecutor")
    void updateRedisViewNumAsync(Long postId,Integer newVal){
        redisCache.setCacheMapValue("post:view-num",postId+"",newVal);
    }

    @Override
    public Integer updateCacheCommentNum(Long postId,Integer increase){
        Integer commentNum = redisCache.getCacheMapValue("post:comment-num",postId+"");
        if (commentNum == null){//若redis没有获取到缓存则从数据库中获取
            Post post = postMapper.selectOne(new LambdaQueryWrapper<Post>().eq(Post::getId,postId));
            if (post != null) {
                commentNum = post.getCommentNum();
            }
        }
        commentNum+=increase;
        updateRedisCommentNumAsync(postId,commentNum);
        return commentNum;
    }

    @Override
    public ResponseResult isLiked(Long userId, Long postId) {
        LambdaQueryWrapper<Like> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Like::getUserId,userId);
        queryWrapper.eq(Like::getLikedType,0);
        queryWrapper.eq(Like::getLikedId,postId);
        Integer count = likeService.count(queryWrapper);
        Map map = new HashMap();
        map.put("isLiked",count>0);
        return ResponseResult.okResult(map);
    }

    @Override
    public ResponseResult searchPost(String title, Long communityId) {
        Page<PostVo> page = new Page<>();
        postMapper.searchPost(page,"%"+title+"%",communityId);
        List<PostVo> postVos = page.getRecords();
        postVos.stream().peek(postVo -> {
            postVo.setTitle(SensitiveWordUtils.replaceWord(postVo.getTitle()));
        }).collect(Collectors.toList());
        PageListVo<PostVo> pageListVo = new PageListVo<>(page.getTotal(),postVos);
        return ResponseResult.okResult(pageListVo);
    }

    @Override
    public ResponseResult isMyPost(Long userId, Long postId) {
        Post post = getById(postId);
        Map<String,Object> resultData = new HashMap<>();
        if (post == null || !post.getCreateBy().equals(userId)){
            resultData.put("isMy",false);
        }else{
            resultData.put("isMy",true);
        }
        return ResponseResult.okResult(resultData);
    }

    @Override
    public ResponseResult updateMyPost(Long userId, Post post) {
        Post oldPost = getById(post.getId());
        if (oldPost== null || !oldPost.getCreateBy().equals(userId)){
            throw new SystemException(AppHttpCodeEnum.FAILURE,"不能修改其他人的帖子");
        }
        updateById(post);
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult deleteMyPost(Long userId, Long postId) {
        Post oldPost = getById(postId);
        if (oldPost== null || !oldPost.getCreateBy().equals(userId)){
            throw new SystemException(AppHttpCodeEnum.FAILURE,"不能删除其他人的帖子");
        }
        removeById(postId);
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult rewardPost(Long userId, Long postId, Integer integral) {
        Post post = getById(postId);
        if (post==null)throw new SystemException(AppHttpCodeEnum.FAILURE,"帖子不存在");
        User sender = userService.getById(userId);
        User accept = userService.getById(post.getCreateBy());
        if (sender==null || accept ==null)throw new SystemException(AppHttpCodeEnum.FAILURE,"用户不存在");
        if (sender.getIntegral() < integral)throw new SystemException(AppHttpCodeEnum.FAILURE,"剩余积分不足");
        sender.setIntegral(sender.getIntegral()-integral);
        accept.setIntegral(accept.getIntegral()+integral);
        List<User> users = new LinkedList<>();
        users.add(accept);
        users.add(sender);
        userService.updateBatchById(users);
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult getMyPostList(Long userId,Integer pageNum, Integer pageSize) {
        Page<PostVo> page = new Page<>();
        postMapper.getMyPostList(page,userId);
        Map<String,Object> map = new HashMap<>();

        //对帖子标题进行屏蔽词过滤
        List<PostVo> safePostList =  page.getRecords().stream().peek(postVo -> {
            String safeTitle = SensitiveWordUtils.replaceWord(postVo.getTitle(),SensitiveWordUtils.root);
            postVo.setTitle(safeTitle);
//            String safeContentPre = SensitiveWordUtils.replaceWord(postVo)
        }).collect(Collectors.toList());

        map.put("rows",safePostList);
        map.put("count",page.getTotal());
        return ResponseResult.okResult(map);
    }


    @Async("redisStatisticsExecutor")
    void updateRedisCommentNumAsync(Long postId,Integer newVal){
        redisCache.setCacheMapValue("post:comment-num",postId+"",newVal);
    }

    public  void updateScoreByView(Long userId, Long postId) {
        System.out.println("userId:"+userId+",postId:"+postId);
        redisTemplate.opsForZSet().incrementScore(SystemConstant.REDIS_PREFIX_RECOMMEND_SCORE+userId,postId,SystemConstant.RECOMMEND_SCORE_FACTOR_VIEW);

    }

    public  void updateScoreByLike(Long userId, Long postId, Integer increase) {
        redisTemplate.opsForZSet().incrementScore(SystemConstant.REDIS_PREFIX_RECOMMEND_SCORE+userId,postId,SystemConstant.RECOMMEND_SCORE_FACTOR_LIKE*increase);

    }

}

